package com.ftwj.demo.config.redis;

import java.time.Duration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
//import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
//import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;

@Configuration
//必须加，使配置生效
@EnableCaching
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class CustomRedisConfig extends CachingConfigurerSupport{
	
	private static final Logger LOGGER = LoggerFactory.getLogger(CustomRedisConfig.class);
    
    @Autowired
    private RedisConnectionFactory factory;
    //private LettuceConnectionFactory factory;
    
    @Bean(name="redisCacheManager")
    @Override
    public RedisCacheManager cacheManager() {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        config = config//.entryTtl(Duration.ZERO)
        		.prefixKeysWith("my-redis-cache::")
        		.entryTtl(Duration.ofDays(7))//设置缓存有效时长24*7小时
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))
                .disableCachingNullValues();
        RedisCacheConfiguration defaultCacheConfig = config//.entryTtl(Duration.ZERO)
        		.prefixKeysWith("my-default-redis-cache::")
        		.entryTtl(Duration.ofDays(7));//设置缓存有效时长24*7小时
        RedisCacheConfiguration wechatCacheConfig = config//.entryTtl(Duration.ZERO)
        		.prefixKeysWith("my-wechat-redis-cache::")
        		.entryTtl(Duration.ofMinutes(30));//设置缓存有效时长30分钟
        // 对每个缓存空间应用不同的配置
        Set<String> cacheNames =  new HashSet<>();
        cacheNames.add("my-redis-cache");
        cacheNames.add("my-wecaht-cache");
        Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
        configMap.put("my-redis-cache", defaultCacheConfig);
        
		configMap.put("my-wecaht-cache", wechatCacheConfig);
        
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
        		//默认配置
        		//.cacheDefaults(config)
        		// 一定要先调用该方法设置初始化的缓存名，再初始化相关的配置
        		.initialCacheNames(cacheNames) 
                .withInitialCacheConfigurations(configMap)
                .build();
        LOGGER.info("初始化缓存配置，创建缓存管理器：成功！");
        LOGGER.info("my-redis-cache: {}",config);
        return cacheManager;
    }
    
    
    /**
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory  redisConnectionFactory) {
        return new RedisCacheManager(
                RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
                this.getRedisCacheConfigurationWithTtl(60*10),//默认策略，为配置的key会使用这个
                
                this.getRedisCacheConfigurationMap() //指定key策略
        );
    }

    private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {
        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
        // 需要作缓存在这里加上就加一个put即可
        redisCacheConfigurationMap.put("my-redis-cache", this.getRedisCacheConfigurationWithTtl(60*60*24*7));
        return redisCacheConfigurationMap;
    }

    private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
        redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(
                RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer())
        ).entryTtl(Duration.ofSeconds(seconds));

        return redisCacheConfiguration;
    }
    */
    
    /**
     * 
     * <p>Title: redisTemplate</p>  
     * <p>Description: 自定义RedisTemplate的K,V序列化方式</p>  
     * @return 
     * @author WangSLi
     * @date 2020年3月17日  
     * @version 1.0
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(){
    	RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
    	
        redisTemplate.setConnectionFactory(factory);
        
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
 
        
        redisTemplate.setKeySerializer(keySerializer());
        redisTemplate.setHashKeySerializer(keySerializer());
        redisTemplate.setValueSerializer(valueSerializer());
        redisTemplate.setHashValueSerializer(valueSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
    
    private RedisSerializer<String> keySerializer() {
        return new StringRedisSerializer();
    }
    private RedisSerializer<Object> valueSerializer() {
        return new GenericJackson2JsonRedisSerializer();
    }
    
    /**
     * 缓存的key是 包名+方法名+参数列表
     */
    @Bean
    @Override
    public KeyGenerator keyGenerator() {
        return (target, method, objects) -> {
            
            StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getName());
            sb.append("::" + method.getName() + "::");
            for (Object obj : objects) {
                sb.append(obj.toString());
            }
            return sb.toString();
            
        };
    }
    
}
